/**
 * Assignment 2 - Pub crawl
 * Authors: Huy Nguyen (3430069)    James Gregory (3288801)
 */
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <mpi.h>
#include <sys/types.h>
#include <unistd.h>

#include "pub.h"

int num_delegates, delegate_sober, bodyguard_sober;
int my_id, my_delegate_group_id;
MPI_Group world_group, delegate_group;
MPI_Comm world_comm, delegate_comm;

void spawn_processors(int argc, char **argv) {
    MPI_Comm parentcomm, intercomm;
    MPI_Comm_get_parent(&parentcomm);
    if(parentcomm == MPI_COMM_NULL) {
        if (argc != 4) {
            MPI_Finalize();
            exit(EXIT_FAILURE);
        }
        int num_procs;
        char *cmd = argv[0];
        MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
        num_delegates = atoi(argv[1]);
        delegate_sober = atoi(argv[2]);
        bodyguard_sober = atoi(argv[3]);
        if(!num_delegates || !delegate_sober || !bodyguard_sober || delegate_sober <= bodyguard_sober) {
            MPI_Finalize();
            exit(EXIT_FAILURE);
        }
        int num_spawn = num_delegates - num_procs + 1;
        if (num_spawn <= 0) {
            MPI_Finalize();
            exit(EXIT_FAILURE);
        }

        int error_codes[num_spawn];
  
        MPI_Comm_spawn(cmd, argv, num_spawn, MPI_INFO_NULL, 0, MPI_COMM_WORLD, &intercomm, error_codes);
        MPI_Intercomm_merge(intercomm, 1, &world_comm);
    } else {
        num_delegates = atoi(argv[2]);
        delegate_sober = atoi(argv[3]);
        bodyguard_sober = atoi(argv[4]);
        MPI_Intercomm_merge(parentcomm, 0, &world_comm);
    }
    MPI_Barrier(world_comm);
    MPI_Comm_group(world_comm, &world_group);
}

void create_delegate_communication_group(void) {
    int *ranks = (int *)malloc(num_delegates * sizeof(int));
    for (int i = 0; i <= num_delegates; i++) {
        ranks[i] = i+1;
    }
    MPI_Group_incl(world_group, num_delegates, ranks, &delegate_group);
    MPI_Comm_create(world_comm, delegate_group, &delegate_comm);
    MPI_Group_rank(delegate_group, &my_delegate_group_id);
    free(ranks);
}

int main(int argc, char **argv) {
    MPI_Init(&argc, &argv);
    srand(time(NULL) ^ (getpid()<<16));
    
    spawn_processors(argc, argv); 
    MPI_Comm_rank(world_comm, &my_id);
    create_delegate_communication_group(); 
  
    if (my_id == ROOT) {
        game_master();
    } else {
        join_game();
    } 

    MPI_Finalize();
    return EXIT_SUCCESS;
}
